package online.chazi.netkit;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelId;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 说明：作者很懒，什么都没留下
 * Created by 叉子同学 on 2020-04-23 19:43
 */
public class SessionGroup {

    //是否自动清除session
    //默认是自动清除
    private boolean cleanAuto = true;

    private final ConcurrentMap<Serializable, Session> sessionMap = new ConcurrentHashMap<>();
    private final ChannelFutureListener remover = future -> {
        List<Serializable> keys = SessionGroup.this.findKeysBySessionId(future.channel().id());
        SessionGroup.this.remove(keys);
    };

    public SessionGroup() {
    }

    public SessionGroup(boolean cleanAuto) {
        this.cleanAuto = cleanAuto;
    }

    public List<Serializable> findKeysBySessionId(ChannelId channelId) {
        List<Serializable> keys = new ArrayList<Serializable>();
        for (Map.Entry<Serializable, Session> entry : this.sessionMap.entrySet()) {
            Session session = entry.getValue();
            if (session.getChannel().id() == channelId) keys.add(entry.getKey());
        }
        return keys;
    }

    public synchronized Session remove(Serializable key) {
        Session s = this.sessionMap.remove(key);
        if (s == null) {
            return null;
        } else if (cleanAuto) {
            s.getChannel().closeFuture().removeListener(this.remover);
        }
        return s;
    }

    public synchronized int remove(Collection<Serializable> keys) {
        int count = 0;
        for (Serializable key : keys) {
            Session s = this.remove(key);
            if (s != null) {
                ++count;
            }
        }
        return count;
    }

    public synchronized boolean add(Serializable key, Session s) {
        if (s != null) {
            Channel channel = s.getChannel();
            boolean added = this.sessionMap.putIfAbsent(key, s) == null;
            if (added && cleanAuto) {
                channel.closeFuture().addListener(this.remover);
            }
            return added;
        } else {
            return false;
        }
    }

    public Session get(Serializable key) {
        return this.sessionMap.get(key);
    }

    public synchronized void close() {
        for (Map.Entry<Serializable, Session> entry : this.sessionMap.entrySet()) {
            Session s = entry.getValue();
            s.close();
        }
        this.sessionMap.clear();
    }

    public boolean isEmpty() {
        return this.sessionMap.isEmpty();
    }

    public int size() {
        return this.sessionMap.size();
    }

    public Collection<Session> sessions() {
        return this.sessionMap.values();
    }

    public Collection<Serializable> keys() {
        return this.sessionMap.keySet();
    }

}
